package com.google.ar.sceneform.samples.hellosceneform.hosh.model

import android.view.MotionEvent
import com.google.ar.sceneform.HitTestResult

import android.content.Context
import com.google.ar.sceneform.Node
import com.google.ar.sceneform.math.Quaternion
import com.google.ar.sceneform.math.Vector3
import com.google.ar.sceneform.rendering.*
import com.google.ar.sceneform.samples.hellosceneform.hosh.ParamKey
import com.google.ar.sceneform.samples.hellosceneform.hosh.model.adapter.ObjCylinderAdapter
import com.google.ar.sceneform.samples.hellosceneform.hosh.model.listener.OnItemClickListener
import kotlin.collections.ArrayList

// 多面体的配置
class ObjCylinderConfig(y : Float) : ObjConfig() {
    var ifDebug = false                     //是否开启调试模式
    val DEFAULT_ROW = 3                     //常量-默认行数
    val DEFAULT_COLUMN = 15                 //常量-默认列数
    var context : Context? = null
    // 默认宽度 1 米
    // 开启递减模式，最大圆所在的行半径为 0.5 米
    // 未开启递减模式，所有行的圆都是 0.5 米
    var y = 1f
        get() = field
        set(value) {
            field = value
        }

    var widthScaleHeight : Float = 3f/4             // 单元格的宽高比（宽屏 3：4）
    var checkSizeScale : Float = 1f                 // 单元格的矫正比例
    var rowDecreaseScale = 0.9f                     // 每一行的尺寸缩小比例，从中间行开始
    var ifRowViewDecreaseScale = true               // 每行尺寸是否缩小的开关
    var checkRowSpace = 0f;                         // 用于调整行间距，根据情况自行设置
    var checkRowScale = 3/7f;                       // 用于调整行间距比例，避免每行行间距过宽，存在逐行递减，所以需要动态根据比例计算
    var objCylinderAdapter : ObjCylinderAdapter? = null // 网格内容适配器
    var tmpModel : ModelRenderable? = null          // 用于标识中心点的调试点
    var debugPointExtent = 0.02f                    // 调试点是一个球体，代表该球体的半径
    private var nodeSet : MutableList<MutableList<Node>> = ArrayList() //管理多面体所有的节点集合
    var listener : OnItemClickListener? = null      // 点击事件监听器

    var row = DEFAULT_ROW                           // 多面体的行数
        get() = field
        set(value) {
            if (value <= 0) {
                field = DEFAULT_ROW
            } else {
                field = value
            }
        }
    var column = DEFAULT_COLUMN                     // 多面体的列数
        get() = field
        set(value) {
            if (value <= 0) {
                field = DEFAULT_COLUMN
            } else {
                field = value
            }
        }

    // 对应列数的角度
    fun getCellRowAngle() : Float {
        return 360f / column
    }

    // 根据列数，计算最大半径所在的行，较为合适的网格宽度，可设置
    var itemWidth = Math.abs(Math.sin(getCellRowAngle().toDouble() / 2).toFloat()) * y/2 * checkSizeScale * 5f/3
    // 根据宽高比，计算最大半径所在的行，较为合适的高度，可以设置宽高比调整
    var itemHeight = itemWidth * widthScaleHeight

    // 构造函数
    init {
        this.y = y
    }

    // 构造函数
    constructor() : this(1f) {
    }

    // 加载调试模式下球体的纹理资源
    fun loadDebugPointModel(resId : Int) {
        Texture.builder().setSource(context, resId).build()
            .thenAccept(
                { texture ->
                    MaterialFactory.makeOpaqueWithTexture(context, texture)
                        .thenAccept { material ->
                            tmpModel = ShapeFactory.makeSphere(debugPointExtent, Vector3(0f, 0f, 0f), material)
                            tmpModel!!.isShadowCaster = false
                        }
                }
            )
    }


    // 构建所有基于父节点的面，从而形成多面体
    fun loadAllFace(parent : Node) {

        // 没有行，不加载任何一个面
        if (row == 0) {
            return
        }
        nodeSet.clear()

        // 找到在当前总行数下，中间行的 ID，中间行可能是一行，也可能是时两行
        var centreRow : MutableList<Int> = findCenterRow()

        // 根据行数，逐行绘制每一行所有的面
        for (i in 0 .. (row  - 1)) {
            // 当前行所有面所在节点的集合
            var tmpList : MutableList<Node> = ArrayList()
            var rowCellSizeScale = caculateCurrentRowSizeScaleInfo(i, centreRow)
            var checkHeight = caculateCurrentRowCheckHeight(i, centreRow)
            var checkAxisX = caculateCurrentRowAxisX(i, centreRow)
            var center = caculateLookAtPostion(checkAxisX, checkHeight, i, centreRow)

            // 逐个构造当前行所有的节点
            for (j in 0 .. (column - 1)) {

                var node = Node()
                node.setParent(parent)
                // 根据当前节点的序号，以及偏转角度，计算当前节点所在位置
                var pos = Vector3(checkAxisX, 0f + checkHeight, 0f)
                val rowRot = Vector3(0f, getCellRowAngle() * j, 0f)
                val rowRotQuaternion = Quaternion.eulerAngles(rowRot)
                val showPos = Quaternion.rotateVector(rowRotQuaternion, pos)

                // 为当前节点加载平面模型
                ViewRenderable.builder()
                    .setView(context, objCylinderAdapter!!.layoutId)
                    .setSizer(DpToMetersViewSizer(ParamKey.DPPERMETER)) // 设置尺寸比例
                    .build()
                    .thenAccept { it ->
                        it.isShadowCaster = false

                        // 通过适配器，获取加载了显示内容的模型
                        if (objCylinderAdapter != null) {
                            var tmp = it//objCylinderAdapter!!.getCellViewRenderable(i, j)
                            // 控制模型中心点的位置是节点，如果不设置，中心点会在模型的底部
                            tmp.horizontalAlignment = ViewRenderable.HorizontalAlignment.CENTER
                            tmp.verticalAlignment = ViewRenderable.VerticalAlignment.CENTER
                            // 设置模型的初始尺寸
                            tmp.view.layoutParams.height = (itemHeight * ParamKey.DPPERMETER).toInt()
                            tmp.view.layoutParams.width = (itemWidth * ParamKey.DPPERMETER).toInt()

                            tmp = objCylinderAdapter!!.getCellViewRenderable(tmp, i, j)
                            node.renderable = tmp
                        }
                        // 设置显示的相对位置
                        node.localPosition = showPos
                        // 根据比例和模型的初始尺寸，设置模型实际的大小
                        node.localScale = Vector3(rowCellSizeScale, rowCellSizeScale, 1f)

                        // 根据中心点，设置模型朝向的方向
                        val direction = Vector3.subtract(center, node.localPosition)
                        node.localRotation = Quaternion.lookRotation(direction, Vector3.up())

                        // 向调用方提供的点击事件的监听器
                        node.setOnTapListener(object : Node.OnTapListener {
                            override fun onTap(p0: HitTestResult?, p1: MotionEvent?) {
                                if (listener != null) {
                                    listener!!.onItemClicked(i , j)
                                }
                            }
                        })
                        tmpList.add(node)
                    }

                if (ifDebug && tmpModel != null) {  // 开启 debug 模式的时候显示中心点的位置
                    var nodeDebug = Node()
                    nodeDebug.setParent(parent)
                    nodeDebug.localPosition = showPos
                    nodeDebug.renderable = tmpModel
                }
            }
            nodeSet.add(tmpList)
        }
    }

    // 每一行的倾角是通过看向指定点的构造出来的
    // 计算每一行对应的朝向的点
    // xLong 每一行的半径
    // checkHeight 每一行所在的高度
    // currentRowNo 当前行号
    // centreRow 中间行的集合
    private fun caculateLookAtPostion(xLong : Float, checkHeight : Float, currentRowNo : Int, centreRow : MutableList<Int>) : Vector3 {
        if (ifRowViewDecreaseScale) {       // 开启递减模式
            var finalAngel = 0f             // 初始的倾斜角度
            var decreaseFoot = 90 * (1 - rowDecreaseScale)   // 每一行的倾角度数
            var finalCenterHeight = checkHeight
            if (currentRowNo > centreRow.last()) {
                finalAngel = decreaseFoot
            } else if (currentRowNo < centreRow[0]) {
                finalAngel = decreaseFoot * -1
            }

            // 根据朝向角度，计算朝向点的位置
            finalCenterHeight += Math.tan(finalAngel.toDouble()).toFloat() * xLong
            return Vector3(0f, 0f + finalCenterHeight, 0f)
        } else {
            return Vector3(0f, 0f + checkHeight, 0f)
        }
    }

    // 找到中间行的行号，可能中间行有两行，可能只有一行
    private fun findCenterRow() : MutableList<Int> {
        var centreRow : MutableList<Int> = ArrayList()
        // 没有唯一中间行
        if (row % 2 == 0) {
            row / 2
            centreRow.add(row / 2 - 1)
            centreRow.add(row / 2)
        } else { // 存在唯一中建行
            centreRow.add(row / 2)
        }
        return centreRow
    }

    // 计算当前行的半径
    private fun caculateCurrentRowAxisX(currentRowNo : Int, centreRow : MutableList<Int>) : Float {
        var xAxisFoot = y / 2                 // 最大半径
        if (ifRowViewDecreaseScale) {               // 开启递减模式
            var ret = 0f;
            if (centreRow.contains(currentRowNo)) {             // 当前行是中间行
                return xAxisFoot * (rowDecreaseScale + 0.05f)   // 微调最大行的半径
//                return xAxisFoot                              // 可直接返回最大行的半径，根据实际情况调整
            } else if (currentRowNo > centreRow.last()) {       // 非中间行，半径按比例指数缩小
                ret = xAxisFoot * Math.pow(rowDecreaseScale.toDouble(), (currentRowNo - centreRow.last()).toDouble()).toFloat()
            } else if (currentRowNo < centreRow[0]) {           // 非中间行，半径按比例指数缩小
                ret = xAxisFoot * Math.pow(rowDecreaseScale.toDouble(), (centreRow[0] - currentRowNo).toDouble()).toFloat()
            }
            return ret
        } else {
            return xAxisFoot        // 未开启递减模式，直接返回最大半径
        }
    }


    // 计算当前行的行高
    private fun caculateCurrentRowCheckHeight(currentRowNo : Int, centreRow : MutableList<Int>) : Float {
        if (!ifRowViewDecreaseScale) {      //未开启递减模式
            var ret = 0f;
            var yAxisFoot = itemHeight * checkRowScale + checkRowSpace
            if (centreRow.size == 2) {  // 双数行，真正中间位置调整
                if (currentRowNo >= centreRow.last()) {
                    ret = (currentRowNo - centreRow.last()) * yAxisFoot
                } else {
                    ret = (currentRowNo - centreRow[0] - 1) * yAxisFoot
                }
            } else {  //单数行，中间行不需要调整
                ret = (currentRowNo - centreRow[0]) * yAxisFoot
            }
            return ret
        } else {        // 开启递减模式
            // 计算当前行与中间行的行差
            var tmp = 0
            if (centreRow.contains(currentRowNo)) {
                if (centreRow.size == 2) {  // 中间行有两行的时候，以行数大的行作为基准行
                    return (currentRowNo - centreRow.last()) * itemHeight * checkRowScale
                } else {
                    return 0f;
                }
            } else if (currentRowNo < centreRow[0]) {
                tmp = currentRowNo - centreRow[0]
            } else {
                tmp = currentRowNo - centreRow.last()
            }

            // 根据行差，计算每一行的高度，每一行的比例都已正数计算，后续会对对称行的正数高度做修正
            var collectRet = 0f
            for (j in 0 .. (Math.abs(tmp) - 1)) {
                collectRet += itemHeight * checkRowScale * Math.pow(rowDecreaseScale.toDouble(), j.toDouble()).toFloat() + checkRowSpace
            }

            // 对以中间行对称行号的高度，取负数修正
            if (tmp < 0) {
                collectRet *= -1
                if (centreRow.size == 2) {
                    collectRet -= itemHeight * checkRowScale
                }
            }
            return collectRet
        }
    }

    // 根据是否尺寸递减开关是否打开，计算每一行单元格的尺寸
    private fun caculateCurrentRowSizeInfo(currentRowNo : Int, centreRow : MutableList<Int>) : RowCellSize {
        var ret = RowCellSize(itemHeight, itemWidth)
        if (ifRowViewDecreaseScale) {       // 尺寸递减开关打开
            var times = caculateCurrentRowSizeScaleInfo(currentRowNo, centreRow)
            ret.cellItemHeight *= times
            ret.cellItemWidth *= times
        }
        return ret
    }

    // 计算当前行网格元素显示的比例
    private fun caculateCurrentRowSizeScaleInfo(currentRowNo : Int, centreRow : MutableList<Int>) : Float {
        var times = 1f
        if (ifRowViewDecreaseScale) {       // 开启尺寸递减
            // 除过中间行，向向下均已 0.9 的比例缩小
            if (currentRowNo < centreRow[0]) {
                times = Math.pow((rowDecreaseScale).toDouble(), (centreRow[0] - currentRowNo).toDouble()).toFloat()
            } else if (currentRowNo > centreRow.last()) {
                times = Math.pow((rowDecreaseScale).toDouble(), (currentRowNo - centreRow.last()).toDouble()).toFloat()
            }
        }
        return times
    }

    data class RowCellSize(var cellItemHeight : Float, var cellItemWidth : Float)
}